use initializer list to construct gpx tag hash. (#509)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Thu, 20 Feb 2020 14:53:15 +0000 (07:53 -0700)
committerGitHub <noreply@github.com>
Thu, 20 Feb 2020 14:53:15 +0000 (07:53 -0700)
gpx.cc
gpx.h

diff --git a/gpx.cc b/gpx.cc
index c2b5033147c1029b9541f939851d12096fd506a9..1084754257a8283d111dca50d036fe2c301c21f5 100644 (file)
--- a/gpx.cc
+++ b/gpx.cc
@@ -70,12 +70,12 @@ GpxFormat::gpx_add_to_global(QStringList& ge, const QString& s)
 // Temporarily mock the old GPX writer's hardcoded fixed length for float/double
 // types.  This can be removed once we have time/interest in regenerating all our
 // zillion reference files.
-inline QString GpxFormat::toString(double d)
+inline QString GpxFormat::toString(double d) const
 {
   return QString::number(d, 'f', 9);
 }
 
-inline QString GpxFormat::toString(float f)
+inline QString GpxFormat::toString(float f) const
 {
   return QString::number(f, 'f', 6);
 }
@@ -102,7 +102,7 @@ GpxFormat::gpx_reset_short_handle()
 }
 
 void
-GpxFormat::gpx_write_gdata(const QStringList& ge, const QString& tag)
+GpxFormat::gpx_write_gdata(const QStringList& ge, const QString& tag) const
 {
   if (!ge.isEmpty()) {
     writer->writeStartElement(tag);
@@ -123,24 +123,11 @@ GpxFormat::gpx_write_gdata(const QStringList& ge, const QString& tag)
   }
 }
 
-GpxFormat::tag_type
-GpxFormat::get_tag(const QString& t, int* passthrough)
+GpxFormat::tag_mapping
+GpxFormat::get_tag(const QString& t) const
 {
-  tag_mapping* tm = hash[t];
-  if (tm) {
-    *passthrough = tm->tag_passthrough;
-    return tm->tag_type_;
-  }
-  *passthrough = 1;
-  return tt_unknown;
-}
-
-void
-GpxFormat::prescan_tags()
-{
-  for (tag_mapping* tm = tag_path_map; tm->tag_type_ != 0; tm++) {
-    hash[tm->tag_name] = tm;
-  }
+  // returns default constructed value if key not found.
+  return hash.value(t);
 }
 
 void
@@ -201,7 +188,7 @@ GpxFormat::tag_cache_desc(const QXmlStreamAttributes& attr)
 }
 
 void
-GpxFormat::tag_gs_cache(const QXmlStreamAttributes& attr)
+GpxFormat::tag_gs_cache(const QXmlStreamAttributes& attr) const
 {
   geocache_data* gc_data = wpt_tmp->AllocGCData();
 
@@ -240,7 +227,7 @@ GpxFormat::start_something_else(const QString& el, const QXmlStreamAttributes& a
    * It was found to be faster to append one element at a time compared to
    *   a) assiging new_tag->attributes = attr, or
    *   b) appending in one step, new_tag.attributes.apppend(attr)
-   * Tested with on ubuntu bioinic with Qt 5.9.5 and a large Geocache file
+   * Tested with on ubuntu bionic with Qt 5.9.5 and a large Geocache file
    * generated by Groundspeak.
    */
   for (const auto& a : attr) {
@@ -293,7 +280,7 @@ GpxFormat::end_something_else()
 }
 
 void
-GpxFormat::tag_log_wpt(const QXmlStreamAttributes& attr)
+GpxFormat::tag_log_wpt(const QXmlStreamAttributes& attr) const
 {
   /* create a new waypoint */
   auto* lwp_tmp = new Waypoint;
@@ -322,15 +309,13 @@ GpxFormat::tag_log_wpt(const QXmlStreamAttributes& attr)
 void
 GpxFormat::gpx_start(const QString& el, const QXmlStreamAttributes& attr)
 {
-  int passthrough;
-
   /*
    * Reset end-of-string without actually emptying/reallocing cdatastr.
    */
   cdatastr = QString();
 
-  int tag = get_tag(current_tag, &passthrough);
-  switch (tag) {
+  tag_mapping tag = get_tag(current_tag);
+  switch (tag.type) {
   case tt_gpx:
     tag_gpx(attr);
     break;
@@ -397,7 +382,7 @@ GpxFormat::gpx_start(const QString& el, const QXmlStreamAttributes& attr)
   default:
     break;
   }
-  if (passthrough) {
+  if (tag.passthrough) {
     start_something_else(el, attr);
   }
 }
@@ -567,15 +552,14 @@ xml_parse_time(const QString& dateTimeString)
 void
 GpxFormat::gpx_end(const QString& /*unused*/)
 {
-  int passthrough;
   static QDateTime gc_log_date;
 
   // Remove leading, trailing whitespace.
   cdatastr = cdatastr.trimmed();
 
-  tag_type tag = get_tag(current_tag, &passthrough);
+  tag_mapping tag = get_tag(current_tag);
 
-  switch (tag) {
+  switch (tag.type) {
   /*
    * First, the tags that are file-global.
    */
@@ -698,7 +682,7 @@ GpxFormat::gpx_end(const QString& /*unused*/)
   case tt_garmin_wpt_country:
   case tt_garmin_wpt_postal_code:
   case tt_garmin_wpt_phone_nr:
-    garmin_fs_xml_convert(tt_garmin_wpt_extensions, tag, cdatastr, wpt_tmp);
+    garmin_fs_xml_convert(tt_garmin_wpt_extensions, tag.type, cdatastr, wpt_tmp);
     break;
 
   /*
@@ -898,7 +882,7 @@ GpxFormat::gpx_end(const QString& /*unused*/)
     break;
   }
 
-  if (passthrough) {
+  if (tag.passthrough) {
     end_something_else();
   }
 
@@ -936,8 +920,6 @@ GpxFormat::rd_init(const QString& fname)
 
   current_tag.clear();
 
-  prescan_tags();
-
   cdatastr = QString();
 
   if (nullptr == gpx_global) {
@@ -1151,7 +1133,7 @@ GpxFormat::read()
 }
 
 void
-GpxFormat::write_attributes(const QXmlStreamAttributes& attributes)
+GpxFormat::write_attributes(const QXmlStreamAttributes& attributes) const
 {
   for (const auto& attribute : attributes) {
     writer->writeAttribute(attribute.qualifiedName().toString(), attribute.value().toString());
@@ -1159,7 +1141,7 @@ GpxFormat::write_attributes(const QXmlStreamAttributes& attributes)
 }
 
 void
-GpxFormat::fprint_xml_chain(xml_tag* tag, const Waypoint* wpt)
+GpxFormat::fprint_xml_chain(xml_tag* tag, const Waypoint* wpt) const
 {
   while (tag) {
     writer->writeStartElement(tag->tagname);
@@ -1194,7 +1176,7 @@ GpxFormat::fprint_xml_chain(xml_tag* tag, const Waypoint* wpt)
  * Handle the grossness of GPX 1.0 vs. 1.1 handling of linky links.
  */
 void
-GpxFormat::write_gpx_url(const UrlList& urls)
+GpxFormat::write_gpx_url(const UrlList& urls) const
 {
   if (gpx_write_version > gpx_1_0) {
     for (const auto& l : urls) {
@@ -1216,7 +1198,7 @@ GpxFormat::write_gpx_url(const UrlList& urls)
 }
 
 void
-GpxFormat::write_gpx_url(const Waypoint* waypointp)
+GpxFormat::write_gpx_url(const Waypoint* waypointp) const
 {
   if (waypointp->HasUrlLink()) {
     write_gpx_url(waypointp->urls);
@@ -1224,7 +1206,7 @@ GpxFormat::write_gpx_url(const Waypoint* waypointp)
 }
 
 void
-GpxFormat::write_gpx_url(const route_head* rh)
+GpxFormat::write_gpx_url(const route_head* rh) const
 {
   if (rh->rte_urls.HasUrlLink()) {
     write_gpx_url(rh->rte_urls);
@@ -1237,7 +1219,7 @@ GpxFormat::write_gpx_url(const route_head* rh)
  * Order counts.
  */
 void
-GpxFormat::gpx_write_common_acc(const Waypoint* waypointp)
+GpxFormat::gpx_write_common_acc(const Waypoint* waypointp) const
 {
   const char* fix = nullptr;
 
@@ -1284,7 +1266,7 @@ GpxFormat::gpx_write_common_acc(const Waypoint* waypointp)
 
 
 void
-GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_type point_type)
+GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_type point_type) const
 {
   if (waypointp->altitude != unknown_alt) {
     writer->writeTextElement(QStringLiteral("ele"), QString::number(waypointp->altitude, 'f', elevation_precision));
@@ -1307,7 +1289,7 @@ GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_
 }
 
 void
-GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_point_type point_type)
+GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_point_type point_type) const
 {
   // gpx version we are writing is >= 1.1.
   if ((opt_humminbirdext && (WAYPT_HAS(waypointp, depth) || WAYPT_HAS(waypointp, temperature))) ||
@@ -1375,7 +1357,7 @@ GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_poin
 }
 
 void
-GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const QString& oname)
+GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const QString& oname) const
 {
   writer->writeOptionalTextElement(QStringLiteral("name"), oname);
 
@@ -1392,7 +1374,7 @@ GpxFormat::gpx_write_common_description(const Waypoint* waypointp, const QString
 }
 
 void
-GpxFormat::gpx_waypt_pr(const Waypoint* waypointp)
+GpxFormat::gpx_waypt_pr(const Waypoint* waypointp) const
 {
   writer->writeStartElement(QStringLiteral("wpt"));
   writer->writeAttribute(QStringLiteral("lat"), toString(waypointp->latitude));
@@ -1460,7 +1442,7 @@ GpxFormat::gpx_track_hdr(const route_head* rte)
 }
 
 void
-GpxFormat::gpx_track_disp(const Waypoint* waypointp)
+GpxFormat::gpx_track_disp(const Waypoint* waypointp) const
 {
   bool first_in_trk = waypointp == current_trk_head->waypoint_list.front();
 
@@ -1524,7 +1506,7 @@ GpxFormat::gpx_track_pr()
 }
 
 void
-GpxFormat::gpx_route_hdr(const route_head* rte)
+GpxFormat::gpx_route_hdr(const route_head* rte) const
 {
   writer->writeStartElement(QStringLiteral("rte"));
   writer->writeOptionalTextElement(QStringLiteral("name"), rte->rte_name);
@@ -1560,7 +1542,7 @@ GpxFormat::gpx_route_hdr(const route_head* rte)
 }
 
 void
-GpxFormat::gpx_route_disp(const Waypoint* waypointp)
+GpxFormat::gpx_route_disp(const Waypoint* waypointp) const
 {
   writer->writeStartElement(QStringLiteral("rtept"));
   writer->writeAttribute(QStringLiteral("lat"), toString(waypointp->latitude));
@@ -1585,7 +1567,7 @@ GpxFormat::gpx_route_disp(const Waypoint* waypointp)
 }
 
 void
-GpxFormat::gpx_route_tlr(const route_head* /*unused*/)
+GpxFormat::gpx_route_tlr(const route_head* /*unused*/) const
 {
   writer->writeEndElement(); // Close rte tag.
 }
diff --git a/gpx.h b/gpx.h
index ab503658678d3c82e57a55410d5df77f592d7243..f1b15685dc6fe0fee94401bffc31765da07344dc 100644 (file)
--- a/gpx.h
+++ b/gpx.h
@@ -181,41 +181,50 @@ private:
     tt_humminbird_trk_trkseg_trkpt_depth,
   };
 
+  struct tag_mapping {
+#if defined(_MSC_VER) && (_MSC_VER < 1910) /* MSVC 2015 or earlier */
+    /* avoid MSVC 2015 C2664 errors. */
+    tag_mapping() = default;
+    tag_mapping(tag_type t, bool p) : type(t),passthrough(p) {}
+#endif
+    tag_type type{tt_unknown};         /* enum from above for this tag */
+    bool passthrough{true};            /* true if we don't generate this */
+  };
+
 
-  void gpx_add_to_global(QStringList& ge, const QString& s);
-  inline QString toString(double d);
-  inline QString toString(float f);
+  static void gpx_add_to_global(QStringList& ge, const QString& s);
+  inline QString toString(double d) const;
+  inline QString toString(float f) const;
   void gpx_reset_short_handle();
-  void gpx_write_gdata(const QStringList& ge, const QString& tag);
-  tag_type get_tag(const QString& t, int* passthrough);
-  void prescan_tags();
+  void gpx_write_gdata(const QStringList& ge, const QString& tag) const;
+  tag_mapping get_tag(const QString& t) const;
   void tag_gpx(const QXmlStreamAttributes& attr);
   void tag_wpt(const QXmlStreamAttributes& attr);
   void tag_cache_desc(const QXmlStreamAttributes& attr);
-  void tag_gs_cache(const QXmlStreamAttributes& attr);
+  void tag_gs_cache(const QXmlStreamAttributes& attr) const;
   void start_something_else(const QString& el, const QXmlStreamAttributes& attr);
   void end_something_else();
-  void tag_log_wpt(const QXmlStreamAttributes& attr);
+  void tag_log_wpt(const QXmlStreamAttributes& attr) const;
   void gpx_start(const QString& el, const QXmlStreamAttributes& attr);
   void gpx_end(const QString& unused);
   void gpx_cdata(const QString& s);
-  void write_attributes(const QXmlStreamAttributes& attributes);
-  void fprint_xml_chain(xml_tag* tag, const Waypoint* wpt);
-  void write_gpx_url(const UrlList& urls);
-  void write_gpx_url(const Waypoint* waypointp);
-  void write_gpx_url(const route_head* rh);
-  void gpx_write_common_acc(const Waypoint* waypointp);
-  void gpx_write_common_position(const Waypoint* waypointp, gpx_point_type point_type);
-  void gpx_write_common_extensions(const Waypoint* waypointp, gpx_point_type point_type);
-  void gpx_write_common_description(const Waypoint* waypointp, const QString& oname);
-  void gpx_waypt_pr(const Waypoint* waypointp);
+  void write_attributes(const QXmlStreamAttributes& attributes) const;
+  void fprint_xml_chain(xml_tag* tag, const Waypoint* wpt) const;
+  void write_gpx_url(const UrlList& urls) const;
+  void write_gpx_url(const Waypoint* waypointp) const;
+  void write_gpx_url(const route_head* rh) const;
+  void gpx_write_common_acc(const Waypoint* waypointp) const;
+  void gpx_write_common_position(const Waypoint* waypointp, gpx_point_type point_type) const;
+  void gpx_write_common_extensions(const Waypoint* waypointp, gpx_point_type point_type) const;
+  void gpx_write_common_description(const Waypoint* waypointp, const QString& oname) const;
+  void gpx_waypt_pr(const Waypoint* waypointp) const;
   void gpx_track_hdr(const route_head* rte);
-  void gpx_track_disp(const Waypoint* waypointp);
+  void gpx_track_disp(const Waypoint* waypointp) const;
   void gpx_track_tlr(const route_head* unused);
   void gpx_track_pr();
-  void gpx_route_hdr(const route_head* rte);
-  void gpx_route_disp(const Waypoint* waypointp);
-  void gpx_route_tlr(const route_head* unused);
+  void gpx_route_hdr(const route_head* rte) const;
+  void gpx_route_disp(const Waypoint* waypointp) const;
+  void gpx_route_tlr(const route_head* unused) const;
   void gpx_route_pr();
   void gpx_waypt_bound_calc(const Waypoint* waypointp);
   void gpx_write_bounds();
@@ -289,12 +298,6 @@ private:
   };
   GpxGlobal* gpx_global = nullptr;
 
-  struct tag_mapping {
-    tag_type tag_type_;                /* enum from above for this tag */
-    int tag_passthrough;               /* true if we don't generate this */
-    const char* tag_name;              /* xpath-ish tag name */
-  };
-
   /*
    * xpath(ish) mappings between full tag paths and internal identifiers.
    * These appear in the order they appear in the GPX specification.
@@ -303,29 +306,18 @@ private:
 
   /* /gpx/<name> for GPX 1.0, /gpx/metadata/<name> for GPX 1.1 */
 #define METATAG(type,name) \
-  {type, 0, "/gpx/" name}, \
-  {type, 0, "/gpx/metadata/" name}
-
-  tag_mapping tag_path_map[158] = {
-    { tt_gpx, 0, "/gpx" },
-    METATAG(tt_name, "name"),
-    METATAG(tt_desc, "desc"),
-    { tt_author, 0, "/gpx/author" },
-    { tt_email, 0, "/gpx/email" },
-    { tt_url, 0, "/gpx/url" },
-    { tt_urlname, 0, "/gpx/urlname" },
-    METATAG(tt_keywords, "keywords"),
-    { tt_link, 0, "/gpx/metadata/link" },
-    { tt_link_text, 0, "/gpx/metadata/link/text" },
-    { tt_link_type, 0, "/gpx/metadata/link/type" },
-
-    { tt_wpt, 0, "/gpx/wpt" },
+  {"/gpx/" name, {type, false}}, \
+  {"/gpx/metadata/" name, {type, false}}
 
-    /* Double up the GPX 1.0 and GPX 1.1 styles */
 #define GEOTAG(type,name) \
-  {type, 1, "/gpx/wpt/groundspeak:cache/groundspeak:" name }, \
-  {type, 1, "/gpx/wpt/extensions/cache/" name }, \
-  {type, 1, "/gpx/wpt/geocache/" name }  /* opencaching.de */
+  {"/gpx/wpt/groundspeak:cache/groundspeak:" name, {type, true}}, \
+  {"/gpx/wpt/extensions/cache/" name, {type, true}}, \
+  {"/gpx/wpt/geocache/" name, {type, true}}    /* opencaching.de */
+
+#define GPXWPTTYPETAG(name,type,passthrough) \
+  {"/gpx/wpt/" name, {type, passthrough}}, \
+  {"/gpx/trk/trkseg/trkpt/" name, {type, passthrough}}, \
+  {"/gpx/rte/rtept/" name, {type, passthrough}}
 
 #define GARMIN_RTE_EXT "/gpx/rte/extensions/gpxx:RouteExtension"
 #define GARMIN_TRK_EXT "/gpx/trk/extensions/gpxx:TrackExtension"
@@ -333,114 +325,121 @@ private:
 #define GARMIN_TRKPT_EXT "/gpx/trk/trkseg/trkpt/extensions/gpxtpx:TrackPointExtension"
 #define GARMIN_RTEPT_EXT "/gpx/rte/rtept/extensions/gpxxx:RoutePointExtension"
 
-//     GEOTAG( tt_cache,               "cache"),
-    { tt_cache, 1, "/gpx/wpt/groundspeak:cache" },
-
-    GEOTAG(tt_cache_name,              "name"),
-    GEOTAG(tt_cache_container,         "container"),
-    GEOTAG(tt_cache_type,              "type"),
-    GEOTAG(tt_cache_difficulty,        "difficulty"),
-    GEOTAG(tt_cache_terrain,   "terrain"),
-    GEOTAG(tt_cache_hint,              "encoded_hints"),
-    GEOTAG(tt_cache_hint,              "hints"),  /* opencaching.de */
-    GEOTAG(tt_cache_desc_short,        "short_description"),
-    GEOTAG(tt_cache_desc_long,         "long_description"),
-    GEOTAG(tt_cache_placer,    "owner"),
-    GEOTAG(tt_cache_favorite_points,   "favorite_points"),
-    GEOTAG(tt_cache_personal_note,     "personal_note"),
-    { tt_cache_log_wpt, 1, "/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:log_wpt"},
-    { tt_cache_log_wpt, 1, "/gpx/wpt/extensions/cache/logs/log/log_wpt"},
-    { tt_cache_log_type, 1, "/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:type"},
-    { tt_cache_log_type, 1, "/gpx/wpt/extensions/cache/logs/log/type"},
-    { tt_cache_log_date, 1, "/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:date"},
-    { tt_cache_log_date, 1, "/gpx/wpt/extensions/cache/logs/log/date"},
-
-    { tt_wpt_extensions, 0, "/gpx/wpt/extensions" },
-
-    { tt_garmin_wpt_extensions, 0, GARMIN_WPT_EXT },
-    { tt_garmin_wpt_proximity, 0, GARMIN_WPT_EXT "/gpxx:Proximity" },
-    { tt_garmin_wpt_temperature, 0, GARMIN_WPT_EXT "/gpxx:Temperature" },
-    { tt_garmin_wpt_temperature, 1, GARMIN_TRKPT_EXT "/gpxtpx:atemp" },
-    { tt_garmin_wpt_depth, 0, GARMIN_WPT_EXT "/gpxx:Depth" },
-    { tt_garmin_wpt_display_mode, 0, GARMIN_WPT_EXT "/gpxx:DisplayMode" },
-    { tt_garmin_wpt_categories, 0, GARMIN_WPT_EXT "/gpxx:Categories" },
-    { tt_garmin_wpt_category, 0, GARMIN_WPT_EXT "/gpxx:Categories/gpxx:Category" },
-    { tt_garmin_wpt_addr, 0, GARMIN_WPT_EXT "/gpxx:Address/gpxx:StreetAddress" },
-    { tt_garmin_wpt_city, 0, GARMIN_WPT_EXT "/gpxx:Address/gpxx:City" },
-    { tt_garmin_wpt_state, 0, GARMIN_WPT_EXT "/gpxx:Address/gpxx:State" },
-    { tt_garmin_wpt_country, 0, GARMIN_WPT_EXT "/gpxx:Address/gpxx:Country" },
-    { tt_garmin_wpt_postal_code, 0, GARMIN_WPT_EXT "/gpxx:Address/gpxx:PostalCode" },
-    { tt_garmin_wpt_phone_nr, 0, GARMIN_WPT_EXT "/gpxx:PhoneNumber"},
+// Maintain a fast mapping from full tag names to the struct above.
+  const QHash<QString, tag_mapping> hash = {
+    {"/gpx", {tt_gpx, false}},
+    METATAG(tt_name, "name"),
+    METATAG(tt_desc, "desc"),
+    {"/gpx/author", {tt_author, false}},
+    {"/gpx/email", {tt_email, false}},
+    {"/gpx/url", {tt_url, false}},
+    {"/gpx/urlname", {tt_urlname, false}},
+    METATAG(tt_keywords, "keywords"),
+    {"/gpx/metadata/link", {tt_link, false}},
+    {"/gpx/metadata/link/text", {tt_link_text, false}},
+    {"/gpx/metadata/link/type", {tt_link_type, false}},
+
+    {"/gpx/wpt", {tt_wpt, false}},
+
+    /* Double up the GPX 1.0 and GPX 1.1 styles */
+//     GEOTAG(tt_cache, "cache"),
+    {"/gpx/wpt/groundspeak:cache", {tt_cache, true}},
+
+    GEOTAG(tt_cache_name, "name"),
+    GEOTAG(tt_cache_container, "container"),
+    GEOTAG(tt_cache_type, "type"),
+    GEOTAG(tt_cache_difficulty, "difficulty"),
+    GEOTAG(tt_cache_terrain, "terrain"),
+    GEOTAG(tt_cache_hint, "encoded_hints"),
+    GEOTAG(tt_cache_hint, "hints"),    /* opencaching.de */
+    GEOTAG(tt_cache_desc_short, "short_description"),
+    GEOTAG(tt_cache_desc_long, "long_description"),
+    GEOTAG(tt_cache_placer, "owner"),
+    GEOTAG(tt_cache_favorite_points, "favorite_points"),
+    GEOTAG(tt_cache_personal_note, "personal_note"),
+    {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:log_wpt", {tt_cache_log_wpt, true}},
+    {"/gpx/wpt/extensions/cache/logs/log/log_wpt", {tt_cache_log_wpt, true}},
+    {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:type", {tt_cache_log_type, true}},
+    {"/gpx/wpt/extensions/cache/logs/log/type", {tt_cache_log_type, true}},
+    {"/gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log/groundspeak:date", {tt_cache_log_date, true}},
+    {"/gpx/wpt/extensions/cache/logs/log/date", {tt_cache_log_date, true}},
+
+    {"/gpx/wpt/extensions", {tt_wpt_extensions, false}},
+
+    {GARMIN_WPT_EXT, {tt_garmin_wpt_extensions, false}},
+    {GARMIN_WPT_EXT "/gpxx:Proximity", {tt_garmin_wpt_proximity, false}},
+    {GARMIN_WPT_EXT "/gpxx:Temperature", {tt_garmin_wpt_temperature, false}},
+    {GARMIN_TRKPT_EXT "/gpxtpx:atemp", {tt_garmin_wpt_temperature, true}},
+    {GARMIN_WPT_EXT "/gpxx:Depth", {tt_garmin_wpt_depth, false}},
+    {GARMIN_WPT_EXT "/gpxx:DisplayMode", {tt_garmin_wpt_display_mode, false}},
+    {GARMIN_WPT_EXT "/gpxx:Categories", {tt_garmin_wpt_categories, false}},
+    {GARMIN_WPT_EXT "/gpxx:Categories/gpxx:Category", {tt_garmin_wpt_category, false}},
+    {GARMIN_WPT_EXT "/gpxx:Address/gpxx:StreetAddress", {tt_garmin_wpt_addr, false}},
+    {GARMIN_WPT_EXT "/gpxx:Address/gpxx:City", {tt_garmin_wpt_city, false}},
+    {GARMIN_WPT_EXT "/gpxx:Address/gpxx:State", {tt_garmin_wpt_state, false}},
+    {GARMIN_WPT_EXT "/gpxx:Address/gpxx:Country", {tt_garmin_wpt_country, false}},
+    {GARMIN_WPT_EXT "/gpxx:Address/gpxx:PostalCode", {tt_garmin_wpt_postal_code, false}},
+    {GARMIN_WPT_EXT "/gpxx:PhoneNumber", {tt_garmin_wpt_phone_nr, false}},
 
     // In Garmin space, but in core of waypoint.
-    { tt_trk_trkseg_trkpt_heartrate, 1, GARMIN_TRKPT_EXT "/gpxtpx:hr" },
-    { tt_trk_trkseg_trkpt_cadence, 1, GARMIN_TRKPT_EXT "/gpxtpx:cad" },
-
-    { tt_humminbird_wpt_depth, 0, "/gpx/wpt/extensions/h:depth" },  // in centimeters.
-    { tt_humminbird_wpt_status, 0, "/gpx/wpt/extensions/h:status" },
-
-    { tt_rte, 0, "/gpx/rte" },
-    { tt_rte_name, 0, "/gpx/rte/name" },
-    { tt_rte_desc, 0, "/gpx/rte/desc" },
-    { tt_rte_url, 0, "/gpx/rte/url"},                          /* GPX 1.0 */
-    { tt_rte_urlname, 0, "/gpx/rte/urlname"},          /* GPX 1.0 */
-    { tt_rte_link, 0, "/gpx/rte/link"},                        /* GPX 1.1 */
-    { tt_rte_link_text, 0, "/gpx/rte/link/text"},      /* GPX 1.1 */
-    { tt_rte_link_type, 0, "/gpx/rte/link/type"},      /* GPX 1.1 */
-    { tt_rte_number, 0, "/gpx/rte/number" },
-    { tt_garmin_rte_display_color, 1, GARMIN_RTE_EXT "/gpxx:DisplayColor"},
-
-    { tt_rte_rtept, 0, "/gpx/rte/rtept" },
-
-    { tt_trk, 0, "/gpx/trk" },
-    { tt_trk_name, 0, "/gpx/trk/name" },
-    { tt_trk_desc, 0, "/gpx/trk/desc" },
-    { tt_trk_trkseg, 0, "/gpx/trk/trkseg" },
-    { tt_trk_url, 0, "/gpx/trk/url"},                          /* GPX 1.0 */
-    { tt_trk_urlname, 0, "/gpx/trk/urlname"},          /* GPX 1.0 */
-    { tt_trk_link, 0, "/gpx/trk/link"},                        /* GPX 1.1 */
-    { tt_trk_link_text, 0, "/gpx/trk/link/text"},      /* GPX 1.1 */
-    { tt_trk_link_type, 0, "/gpx/trk/link/type"},      /* GPX 1.1 */
-    { tt_trk_number, 0, "/gpx/trk/number" },
-    { tt_garmin_trk_display_color, 1, GARMIN_TRK_EXT "/gpxx:DisplayColor"},
-
-    { tt_trk_trkseg_trkpt, 0, "/gpx/trk/trkseg/trkpt" },
-    { tt_trk_trkseg_trkpt_course, 0, "/gpx/trk/trkseg/trkpt/course" },
-    { tt_trk_trkseg_trkpt_speed, 0, "/gpx/trk/trkseg/trkpt/speed" },
-
-    { tt_humminbird_trk_trkseg_trkpt_depth, 0, "/gpx/trk/trkseg/trkpt/extensions/h:depth" },  // in centimeters.
+    {GARMIN_TRKPT_EXT "/gpxtpx:hr", {tt_trk_trkseg_trkpt_heartrate, true}},
+    {GARMIN_TRKPT_EXT "/gpxtpx:cad", {tt_trk_trkseg_trkpt_cadence, true}},
+
+    {"/gpx/wpt/extensions/h:depth", {tt_humminbird_wpt_depth, false}}, // in centimeters.
+    {"/gpx/wpt/extensions/h:status", {tt_humminbird_wpt_status, false}},
+
+    {"/gpx/rte", {tt_rte, false}},
+    {"/gpx/rte/name", {tt_rte_name, false}},
+    {"/gpx/rte/desc", {tt_rte_desc, false}},
+    {"/gpx/rte/url", {tt_rte_url, false}},                                                     /* GPX 1.0 */
+    {"/gpx/rte/urlname", {tt_rte_urlname, false}},                     /* GPX 1.0 */
+    {"/gpx/rte/link", {tt_rte_link, false}},                                           /* GPX 1.1 */
+    {"/gpx/rte/link/text", {tt_rte_link_text, false}}, /* GPX 1.1 */
+    {"/gpx/rte/link/type", {tt_rte_link_type, false}}, /* GPX 1.1 */
+    {"/gpx/rte/number", {tt_rte_number, false}},
+    {GARMIN_RTE_EXT "/gpxx:DisplayColor", {tt_garmin_rte_display_color, true}},
+
+    {"/gpx/rte/rtept", {tt_rte_rtept, false}},
+
+    {"/gpx/trk", {tt_trk, false}},
+    {"/gpx/trk/name", {tt_trk_name, false}},
+    {"/gpx/trk/desc", {tt_trk_desc, false}},
+    {"/gpx/trk/trkseg", {tt_trk_trkseg, false}},
+    {"/gpx/trk/url", {tt_trk_url, false}},                                                     /* GPX 1.0 */
+    {"/gpx/trk/urlname", {tt_trk_urlname, false}},                     /* GPX 1.0 */
+    {"/gpx/trk/link", {tt_trk_link, false}},                                           /* GPX 1.1 */
+    {"/gpx/trk/link/text", {tt_trk_link_text, false}}, /* GPX 1.1 */
+    {"/gpx/trk/link/type", {tt_trk_link_type, false}}, /* GPX 1.1 */
+    {"/gpx/trk/number", {tt_trk_number, false}},
+    {GARMIN_TRK_EXT "/gpxx:DisplayColor", {tt_garmin_trk_display_color, true}},
+
+    {"/gpx/trk/trkseg/trkpt", {tt_trk_trkseg_trkpt, false}},
+    {"/gpx/trk/trkseg/trkpt/course", {tt_trk_trkseg_trkpt_course, false}},
+    {"/gpx/trk/trkseg/trkpt/speed", {tt_trk_trkseg_trkpt_speed, false}},
+
+    {"/gpx/trk/trkseg/trkpt/extensions/h:depth", {tt_humminbird_trk_trkseg_trkpt_depth, false}},       // in centimeters.
 
     /* Common to tracks, routes, and waypts */
-#define GPXWPTTYPETAG(type,passthrough,name) \
-  {type, passthrough, "/gpx/wpt/" name }, \
-  {type, passthrough, "/gpx/trk/trkseg/trkpt/" name }, \
-  {type, passthrough, "/gpx/rte/rtept/" name }
-
-    GPXWPTTYPETAG(tt_wpttype_ele, 0, "ele"),
-    GPXWPTTYPETAG(tt_wpttype_time, 0, "time"),
-    GPXWPTTYPETAG(tt_wpttype_geoidheight, 0, "geoidheight"),
-    GPXWPTTYPETAG(tt_wpttype_name, 0, "name"),
-    GPXWPTTYPETAG(tt_wpttype_cmt, 0, "cmt"),
-    GPXWPTTYPETAG(tt_wpttype_desc, 0, "desc"),
-    GPXWPTTYPETAG(tt_wpttype_url, 0, "url"),                           /* GPX 1.0 */
-    GPXWPTTYPETAG(tt_wpttype_urlname, 0, "urlname"),           /* GPX 1.0 */
-    GPXWPTTYPETAG(tt_wpttype_link, 0, "link"),                 /* GPX 1.1 */
-    GPXWPTTYPETAG(tt_wpttype_link_text, 0, "link/text"),       /* GPX 1.1 */
-    GPXWPTTYPETAG(tt_wpttype_link_type, 0, "link/type"),       /* GPX 1.1 */
-    GPXWPTTYPETAG(tt_wpttype_sym, 0, "sym"),
-    GPXWPTTYPETAG(tt_wpttype_type, 1, "type"),
-    GPXWPTTYPETAG(tt_wpttype_fix, 0, "fix"),
-    GPXWPTTYPETAG(tt_wpttype_sat, 0, "sat"),
-    GPXWPTTYPETAG(tt_wpttype_hdop, 0, "hdop"),
-    GPXWPTTYPETAG(tt_wpttype_vdop, 0, "vdop"),
-    GPXWPTTYPETAG(tt_wpttype_pdop, 0, "pdop"),
-
-    {(tag_type)0, 0, nullptr}
+    GPXWPTTYPETAG("ele", tt_wpttype_ele, false),
+    GPXWPTTYPETAG("time", tt_wpttype_time, false),
+    GPXWPTTYPETAG("geoidheight", tt_wpttype_geoidheight, false),
+    GPXWPTTYPETAG("name", tt_wpttype_name, false),
+    GPXWPTTYPETAG("cmt", tt_wpttype_cmt, false),
+    GPXWPTTYPETAG("desc", tt_wpttype_desc, false),
+    GPXWPTTYPETAG("url", tt_wpttype_url, false),                                                       /* GPX 1.0 */
+    GPXWPTTYPETAG("urlname", tt_wpttype_urlname, false),                       /* GPX 1.0 */
+    GPXWPTTYPETAG("link", tt_wpttype_link, false),                                             /* GPX 1.1 */
+    GPXWPTTYPETAG("link/text", tt_wpttype_link_text, false),   /* GPX 1.1 */
+    GPXWPTTYPETAG("link/type", tt_wpttype_link_type, false),   /* GPX 1.1 */
+    GPXWPTTYPETAG("sym", tt_wpttype_sym, false),
+    GPXWPTTYPETAG("type", tt_wpttype_type, true),
+    GPXWPTTYPETAG("fix", tt_wpttype_fix, false),
+    GPXWPTTYPETAG("sat", tt_wpttype_sat, false),
+    GPXWPTTYPETAG("hdop", tt_wpttype_hdop, false),
+    GPXWPTTYPETAG("vdop", tt_wpttype_vdop, false),
+    GPXWPTTYPETAG("pdop", tt_wpttype_pdop, false),
   };
 
-// Maintain a fast mapping from full tag names to the struct above.
-  QHash<QString, tag_mapping*> hash;
-
   QVector<arglist_t> gpx_args = {
     {
       "snlen", &snlen, "Length of generated shortnames",